#!/usr/bin/env perl 


use strict;
use Getopt::Long;
use Data::Dumper;
use Cwd;
use Cwd "realpath";
use File::Basename;

use constant version => "0.1.0";


sub usage
{
	print "Kiwiphp - " . version . "(Apr 3,2009)\n\n";
	print "Usage: \n";
	print " kiwiphp <options> \n";
	print "  -help print this help information \n";
	print "  -init <directory> kiwiphp will set <directory> as proj_root \n";
	print "     to create the project directories and files, and it will \n";
	print "     create a .kiwi file in <directory> to save the project information\n";
	print "  -addapp <application> \n";
	print "  -addmodule <application>::<module>\n";
	print "  -addaction <application>::<module>::<action>\n";
	print "  -addcomponent <application>::<module>::<component>\n";
	print "  -adddao <dao>\n";
	print "  -addservice <service>\n";
	print "  -kiwidir <directory>\n\n";
	exit(0);

}

sub kiwiError
{
	my ($info) = @_;
	print "Kiwiphp error: " . $info . "\n";
	exit(0);
}

sub kiwiMsg
{
	my($msg) = @_;
	print "Kiwiphp msg: " . $msg . "\n";
}
my ($help, $projDir, $app, $module, $action, $component, $dao, $service, $kiwidir);

GetOptions(
	"help" => \$help,
	"init=s" => \$projDir,
	"addapp=s" => \$app,
	"addmodule=s" => \$module,
	"addaction=s" => \$action,
	"addcomponent=s" => \$component,
	"adddao=s" => \$dao,
	"addservice=s" => \$service,
	"kiwidir=s" => \$kiwidir
) or parameterError("Please check the parameters.");


usage if $help;
my $kiwiInfoFile;
if ($projDir) {
	$projDir = realpath($projDir);
	addProject($projDir);
}

if($app) {
	$projDir = $projDir || findProjDir();
	kiwiMsg("Current proj_dir is " . $projDir);
	addApp($projDir, $app);
}

if($module) {
	$projDir = $projDir || findProjDir();
	addModule($projDir, $module);
}

if($action) {
	$projDir = $projDir || findProjDir();
	addAction($projDir, $action);
}
if($component) {
	$projDir = $projDir || findProjDir();
	addComponent($projDir, $component);
}

if(!$projDir) {
	usage();
}

sub adddao
{
	my($dao) = @_;

}
sub addservice
{
	my($service) = @_;
}

sub addProject
{
	my($projDir) = @_;

	if(!checkWrite($projDir,"d")) {
		if(!makeDir($projDir)) {
			kiwiError("Cann't create directory: '$projDir'");	
		}	
	}

	$kiwiInfoFile = $projDir . "/.kiwi";

	my %dirs = (
		"entry" => 0755,
		"app" => 0755,
		"conf" => 0755,
		"data" => 0755,
		"dist" => 0777,
		"lib" => 0755,
		"lib/addon" => 0755,
		"lib/util" => 0755,
		"lib/dao" => 0755,
		"lib/service" => 0755
	);
	my %files = (
		"conf/conf.php" => <<CODE 
<?php

/**
 * This is the config file.
 *  You can write the configuration in this file.
 *  format: \$config['xxxx'] = "xxxx";
 *  In the program you can use Config::\$proj['xxxx'] to get the value;
 **/
\$config = array();
// NOTICE: config begin
// XXX Make sure write config code here.


// NOTICE: config end.
return \$config;
CODE
		,
		".kiwi" => ""
	);

	makeDirs($projDir, \%dirs);
	makeFiles($projDir, \%files);
}
sub checkApp
{
	my($projDir, $app) = @_;
	if(-e $projDir . "/app/$app/") {
		return 1;
	}
	addApp($projDir, $app);
}
sub addApp
{
	my($projDir, $app) = @_;
	checkName($app, "app");
	my %dirs = (
		"app/$app" => 0755,
		"app/$app/module" => 0755,
		"app/$app/lib" => 0755,
		"app/$app/layout" => 0755,
		"app/$app/module/index" => 0755,
		"app/$app/module/index/action" => 0755,
		"app/$app/module/index/view" => 0755,	
		"app/$app/lib/component" => 0755,
		"entry/$app" => 0755,
		"entry/$app/js" => 0755,
		"entry/$app/css" => 0755,
		"entry/$app/images" => 0755
	);
	my $kiwifile = $kiwidir ? realpath($kiwidir) . "/runtime/kiwi.php" : "/path/to/kiwiphp/runtime/kiwi.php";
	my %files = (
		"entry/$app/$app.php" => <<CODE
<?php
require('$kiwifile');
\$options = array(
	'proj_dir' => '$projDir'
);
Kiwi::run(\$options);
CODE
	,
	"app/$app/layout/layout.php" => <<CODE
<?php
include(\$this->getTemplateDir() . \$this->getTemplate() . '.php');
CODE

	);
	makeDirs($projDir, \%dirs);
	makeFiles($projDir, \%files);
	addModule($projDir,"${app}::index");
	addPreDefineFunc($projDir);
}

sub checkModule
{
	my($projdir, $app, $module) = @_;
	if( -e $projDir . "/app/$app/module/$module/") {
		return 1;
	}
	addModule($projDir, "${app}::${module}");
}
sub addModule
{
	my($projDir, $_module) = @_;
	my($app, $module) = split(/::/, $_module);
	checkName($app,"app");
	checkName($module, "module");
	checkApp($projDir, $app);
	my %dirs = (
		"app/$app/module/$module" => 0755,
		"app/$app/module/$module/view" => 0755,
		"app/$app/module/$module/action" => 0755	
	);
	makeDirs($projDir, \%dirs);
	addAction($projDir, "${app}::${module}::index");
}

sub addAction
{
	my($projDir, $_action) = @_;
	my($app, $module, $action) = split(/::/, $_action);

	checkName($app, "app");
	checkName($module, "module");
	checkName($action, "action");
	checkModule($projDir, $app,$module);
	my $datetime = getDatetime();
	my %files = (
		"app/$app/module/$module/action/${action}Action.php" => <<CODE
<?php
/**
 * This file is auto generated by kiwiphp.
 *  
 *  \@class ${action}Action
 *  \@author xxxx (xxx\@xxx.com)
 *  \@date $datetime
 */

class ${action}Action extends Action
{
	public function execute()
	{
		\$this->view->setLayout("layout");
		\$this->view->render();
	}
}
CODE
			,
			"app/$app/module/$module/view/${action}.php" => <<CODE
Hi, the example is running.
CODE
			,
	);
	makeFiles($projDir, \%files);
}

sub addComponent
{
	my($projDir, $_component) = @_;
	my($app, $module, $component) = split(/::/, $_component);
	checkName($app, "app");
	checkName($module, "module");
	checkName($component, "component");
	checkModule($projDir,$app,$module);
	my $datetime = getDatetime();
	my %files = (
		"app/$app/lib/component/${component}Component.php" => <<CODE
<?php
/**
 * This file is auto generated by kiwiphp.
 *  
 *  \@class ${component}Component 
 *  \@author xxxx (xxx\@xxx.com)
 *  \@date $datetime
 */

class ${component}Component extends Component 
{
	public function execute()
	{
		\$this->view->setLayout("layout");
		\$this->view->render();
	}
}
CODE
			,
			"app/$app/module/$module/view/${component}.php" => <<CODE
Hi, the example $module::$component is running.<br>
CODE
			,
	);
	makeFiles($projDir, \%files);
}
sub makeDirs
{
	my($projDir, $dirs) = @_;

	for (sort(keys(%$dirs))) {
		kiwiMsg("Make dir " .$_);
		if(!makeDir($projDir . "/" . $_, $dirs->{$_})) {
			kiwiError("Make dir " . $_ . " error");
		}
	}
}

sub makeFiles
{
	my($projDir, $files) = @_;
	for(sort(keys(%$files))) {
		if( ! -e $projDir . "/" . $_) {
			kiwiMsg("Write file " . $_);
			open F, ">",  $projDir . "/" . $_ or kiwiError("write file $_ error");
			print F $files->{$_};
			close F;
		} else {
			kiwiMsg("file $_ exists!");
		}
	}
}

sub findProjDir
{
	my $cwd = getcwd();
	while($cwd ne '/') 
	{
		last if(checkRead($cwd . '/' . '.kiwi'));
		$cwd = dirname($cwd);
	}
	
	if(checkRead($cwd . '/' . '.kiwi')) {
		return $cwd;
	}

	kiwiError("Cann't find proj directory, make sure you are under proj directory");
	return 0;
}

sub getOptions
{
	my ($kiwiInfoFile) = @_;
	if (checkRead($kiwiInfoFile)) {
		my %options;
		open F, "<$kiwiInfoFile";
		while(<F>) {
			chomp;
			s/^\s*|\s*$//;
			next if /^#/ || /^\s*$/;
			my($key, $value) = split(/\s*=\s*/);
			$options{$key} = $value;
		}	
		return %options;
	}
	return (
		"proj_dir" => dirname($kiwiInfoFile),
		"info_file" => $kiwiInfoFile
	);
}

sub checkName
{
	my($name, $info) = @_;
	if($name =~ /^[\w][\w\d]*$/) {
		return 1;
	} 
	kiwiError("The given $info '$name' is invalid, please make sure it fits '/^[\w][\w\d]*\$/");
}
sub checkWrite
{
	my ($file, $mode) = @_;

	if(! -e $file || ! -w $file) {
		return 0;	
	}
	return 0 if ($mode eq "d") && (! -d $file);
	return 0 if ($mode eq "f") && (! -f $file);
	return 1;
}
sub checkRead
{
	my ($file) = @_;
	return (-r $file && -f $file)  ? 1 : 0;
}
sub makeDir
{
	my ($dir,$mode) = @_;
	if( ! -d $dir) {
		return mkdir $dir, ($mode ? $mode : 0755);
	} 
	kiwiMsg("directory $dir exists!");
}
sub getDatetime
{
	my($sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst) = localtime();
	return ($year + 1900) . '-' . ($month + 1) . '-' . $day . ' ' . $hour . ':' . $min;
}
sub addPreDefineFunc
{
	my($projDir)= @_;
	my %files = (
		"lib/pre_define_function.php" => <<CODE
<?php
/**
 * 
 * Action not found hooks.
 **/
function onActionNotFound(\$module, \$action)
{
  echo "module:\$module, action:\$action not found !";
	exit(0);
}
CODE
	);
	makeFiles($projDir, \%files);
}
